package com.adams.custom.page;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.InitializingBean;

import com.adams.access.db.bean.ExecSqlScriptInfo;
import com.adams.access.db.bean.SystemVersion;
import com.adams.core.exception.BusinessException;
import com.adams.custom.page.core.service.BusinessDealFlowEngineService;
import com.adams.custom.page.core.service.DicTableService;
import com.adams.custom.page.core.service.ExecSqlScriptInfoService;
import com.adams.custom.page.core.service.SystemVersionService;
import com.adams.custom.page.mgt.service.ViewCfgService;
import com.adams.custom.page.mgt.service.ViewParseService;
import com.adams.custom.page.util.ViewUtil;
import com.adams.utils.ResourceUtil;

/**
 * 
 * File Name : InitCache.java
 * 
 * @Description : 初始化缓存
 */
public class ViewInit implements InitializingBean
{

	private static Logger logger = Logger.getLogger(ViewInit.class.getName());

	/**
	 * Description : 执行数据库脚本信息服务接口 Author : 904370
	 */
	private ExecSqlScriptInfoService execSqlScriptInfoService;

	/**
	 * Description : 系统版本信息服务接口 Author : 904370
	 */
	private SystemVersionService systemVersionService;
	/**
	 * Description : 数据字典服务接口 Author : 904370
	 */
	private DicTableService dicTableService;

	/**
	 * Description : 界面定义服务接口 Author : 904370
	 */
	private ViewCfgService viewCfgService;
	/**
	 * Description : 界面服务接口 Author : 904370
	 */
	private ViewParseService viewParseService;

	/**
	 * Description : 业务处理流调用引擎服务类 Author : 904370
	 */
	private BusinessDealFlowEngineService businessDealFlowEngineService;

	/**
	 * Description : 执行_系统启动初始化服务_标识
	 * 值为jdbc.properties文件的adams.exce.initsql.flag值决定，如果没有此参数或者此参数不等于true，都不执行
	 * Author : 904370
	 */
	private Boolean exceInitSql;

	public void setExceInitSql(Boolean exceInitSql)
	{
		this.exceInitSql = exceInitSql;
	}

	public void setExecSqlScriptInfoService(
			ExecSqlScriptInfoService execSqlScriptInfoService)
	{
		this.execSqlScriptInfoService = execSqlScriptInfoService;
	}

	public void setSystemVersionService(
			SystemVersionService systemVersionService)
	{
		this.systemVersionService = systemVersionService;
	}

	public void setViewCfgService(ViewCfgService viewCfgService)
	{
		this.viewCfgService = viewCfgService;
	}

	public void setDicTableService(DicTableService dicTableService)
	{
		this.dicTableService = dicTableService;
	}

	public void setViewParseService(ViewParseService viewParseService)
	{
		this.viewParseService = viewParseService;
	}

	public void setBusinessDealFlowEngineService(
			BusinessDealFlowEngineService businessDealFlowEngineService)
	{
		this.businessDealFlowEngineService = businessDealFlowEngineService;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception
	{
		if (exceInitSql)
		{
			execSystemVersionUpdate();
			execSystemStartUpInit();
		}
		try
		{
			businessDealFlowEngineService.synchronizationCache();
			dicTableService.synchronizationCache();
			viewCfgService.synchronizationCache();
			viewParseService.synchronizationCache();
		} catch (Exception e)
		{
			logger.error("init failure", e);
			try
			{
				viewParseService.clearCache();
			} catch (Exception ex)
			{
				logger.error("清除缓存出错！", ex);
			}
		}
	}

	private static String ADAMS_VERSION_FILE = "com/adams/systemversion.xml";

	/**
	 * Description : 执行系统版本升级功能
	 * 
	 * 
	 */
	@SuppressWarnings("unchecked")
	public void execSystemVersionUpdate() throws Exception
	{
		// 当前数据库系统版本号
		String dbVersionNumber = null;
		// 数据库系统版本对象
		SystemVersion adamsSystemVersion = null;
		// 当前代码系统版本号
		String codeVersionNumber = null;

		// 获得当前数据库版本对象
		List<SystemVersion> systemVersions = systemVersionService
				.findAllSystemVersion();
		for (SystemVersion systemVersion : systemVersions)
		{
			if (SystemVersion.ADAMS_SYSTEM_NAME.equals(systemVersion.getName()))
			{
				dbVersionNumber = systemVersion.getVersionNumber();
				adamsSystemVersion = systemVersion;
				break;
			}
		}

		if (ViewUtil.isNuLLStr(dbVersionNumber))
		{
			return;
		}

		// versionMap存储版本与版本的先后关系，版本下所有的按顺序的升级脚本
		LinkedHashMap<String, List<String>> versionMap = new LinkedHashMap<String, List<String>>();
		// 获得系统版本关系信息
		URL url = Thread.currentThread().getContextClassLoader().getResource(
				ADAMS_VERSION_FILE);
		try
		{
			InputStream io = url.openStream();
			SAXReader saxReader = new SAXReader();
			Document doc = saxReader.read(io);
			Element root = doc.getRootElement();
			String defaultpack = root.attributeValue("defaultpack");
			if (null == defaultpack)
			{
				defaultpack = "";
			} else
			{
				defaultpack = defaultpack.replace(ResourceUtil.PACK_SEP,
						ResourceUtil.PACK_FILE_SEP);
			}
			List<Element> versions = root.elements();
			int versionsLen = versions.size();

			// 因为脚本升级的次数总少于系统启动的次数,所以先判断版本统一性
			// 最后一条为当前代码的系统版本号
			codeVersionNumber = versions.get(versionsLen - 1).attributeValue(
					"number");
			// 如果当前代码版本与数据库版本一致，不进行更新操作
			if (!ViewUtil.isNuLLStr(codeVersionNumber)
					&& codeVersionNumber.equals(dbVersionNumber))
			{
				return;
			}

			for (int i = 0; i < versionsLen; i++)
			{
				Element version = versions.get(i);
				String number = version.attributeValue("number");
				if (ViewUtil.isNuLLStr(number))
				{
					continue;
				}

				String pack = version.attributeValue("pack");
				if (ViewUtil.isNuLLStr(pack))
				{
					pack = defaultpack;
				} else
				{
					pack = pack.replace(ResourceUtil.PACK_SEP,
							ResourceUtil.PACK_FILE_SEP);
				}
				List<Element> sqls = version.elements("sql");
				int sqlsLen = 0;
				if (null != sqls)
				{
					sqlsLen = sqls.size();
				}
				List<String> sqlfiles = new ArrayList<String>();
				for (int j = 0; j < sqlsLen; j++)
				{
					Element sql = sqls.get(j);
					String sqlfilename = sql.getText();
					String sqlfile = "";
					if (ViewUtil.isNuLLStr(sqlfilename))
					{
						continue;
					}

					if (ViewUtil.isNuLLStr(pack))
					{
						sqlfile = sqlfilename;
					} else
					{
						sqlfile = pack + "/" + sqlfilename;
					}
					sqlfiles.add(sqlfile);
				}
				versionMap.put(number, sqlfiles);
			}
		} catch (IOException e)
		{
			logger.error("读取" + ADAMS_VERSION_FILE + "文件出错！", e);
			throw e;
		} catch (DocumentException e)
		{
			logger.error(ADAMS_VERSION_FILE + "不符合xml格式！", e);
			throw e;
		}

		// 当前代码版本与数据库版本不一致，进行更新操作
		Set<String> numberSet = versionMap.keySet();
		boolean execSqlflag = false;
		for (Iterator<String> numberIt = numberSet.iterator(); numberIt
				.hasNext();)
		{
			String number = numberIt.next();
			if (!execSqlflag && dbVersionNumber.equals(number))
			{
				// 当前版本，下一个版本进行更新操作
				execSqlflag = true;
				continue;
			}
			if (!execSqlflag)
			{
				// 以前版本不进行升级操作
				continue;
			}

			// 拼装此版本下所有要更新的sql脚本
			List<String> sqlfiles = versionMap.get(number);
			StringBuilder sqlScript = new StringBuilder();
			if (null != sqlfiles && sqlfiles.size() > 0)
			{
				for (String sqlfile : sqlfiles)
				{
					URL fileUrl = Thread.currentThread()
							.getContextClassLoader().getResource(sqlfile);
					try
					{
						String sql = ViewInit.getSqlScript(fileUrl, sqlfile);
						sqlScript.append(sql);
						sqlScript.append("\r\n");
					} catch (Exception e)
					{
						logger.error("数据库升级脚本文件:" + sqlfile + "不存在!不能升级到"
								+ number + "版本!最终系统代码版本与数据库版本不一致!系统启动失败!");
						throw e;
					}
				}
			}

			// 执行数据库更新操作
			String currentVersionNumber = adamsSystemVersion.getVersionNumber();
			try
			{
				adamsSystemVersion.setVersionNumber(number);
				adamsSystemVersion = systemVersionService
						.saveOrUpdateSystemVersion(adamsSystemVersion,
								sqlScript.toString());
			} catch (BusinessException e)
			{
				logger.error("系统从" + currentVersionNumber + "版本自动升级到" + number
						+ "版本出错!最终系统代码版本与数据库版本不一致!系统启动失败!", e);
				throw e;
			}
		}
	}

	/**
	 * Description : 系统启动初始化服务
	 * 
	 * 
	 */
	public void execSystemStartUpInit()
	{
		List<ExecSqlScriptInfo> sqls = execSqlScriptInfoService
				.findAllExecSqlScriptInfo();
		Map<String, ExecSqlScriptInfo> sqlMap = new HashMap<String, ExecSqlScriptInfo>();
		for (ExecSqlScriptInfo sqlObject : sqls)
		{
			sqlMap.put(sqlObject.getBusinessname(), sqlObject);
		}
		Set<String> packs = ResourceUtil.ADAMS_BUSINESS_PACK;
		// 保存业务名,用于下面反安装操作
		Set<String> businessnames = new HashSet<String>();

		for (String pack : packs)
		{
			String businessname = pack.substring(ResourceUtil.ADAMS_PACK
					.length() + 1, pack.length());
			businessnames.add(businessname);
			ExecSqlScriptInfo sqlObject = null;
			if (sqlMap.containsKey(businessname))
			{
				sqlObject = sqlMap.get(businessname);
			} else
			{
				sqlObject = new ExecSqlScriptInfo();
				sqlObject.setBusinessname(businessname);
			}
			StringBuilder sqlpackSB = new StringBuilder();
			sqlpackSB.append(pack);
			sqlpackSB.append(ResourceUtil.PACK_FILE_SEP);
			sqlpackSB.append("sql");
			sqlpackSB.append(ResourceUtil.PACK_FILE_SEP);
			sqlpackSB.append(businessname);
			String sqlpackStr = sqlpackSB.toString();
			String initSql = sqlpackStr + "_init.sql";
			String adamsinstallsql = sqlpackStr + "_install.sql";
			String adamsuninstallsql = sqlpackStr + "_uninstall.sql";
			URL fileUrl = null;
			boolean initsucess = true;
			if (ViewUtil.isNuLLStr(sqlObject.getInitsql()))
			{
				fileUrl = Thread.currentThread().getContextClassLoader()
						.getResource(initSql);
				if (null != fileUrl)
				{

					sqlObject.setInitsql(initSql);
					try
					{
						String sqlScript = getSqlScript(fileUrl, initSql);
						sqlObject = execSqlScriptInfoService
								.saveOrUpdateExecSqlScriptInfo(sqlObject,
										sqlScript);
					} catch (BusinessException e)
					{
						logger.error("执行" + initSql + "脚本出错！", e);
						sqlObject.setInitsql(null);
						initsucess = false;
					} catch (Exception e)
					{
						// 执行脚本失败
						logger.error("保存执行sql脚本信息对象失败！", e);
						sqlObject.setInitsql(null);
						initsucess = false;
					}
				}
			}
			if (initsucess
					&& ViewUtil.isNuLLStr(sqlObject.getAdamsinstallsql()))
			{
				fileUrl = Thread.currentThread().getContextClassLoader()
						.getResource(adamsinstallsql);
				if (null != fileUrl)
				{
					sqlObject.setAdamsinstallsql(adamsinstallsql);
					try
					{
						String sqlScript = getSqlScript(fileUrl,
								adamsinstallsql);
						sqlObject = execSqlScriptInfoService
								.saveOrUpdateExecSqlScriptInfo(sqlObject,
										sqlScript);
					} catch (BusinessException e)
					{
						logger.error("执行" + adamsinstallsql + "脚本出错！", e);
						sqlObject.setAdamsinstallsql(null);
					} catch (Exception e)
					{
						// 执行脚本失败
						logger.error("保存执行sql脚本信息对象失败！", e);
						sqlObject.setAdamsinstallsql(null);
					}

				}
			}
			if (ViewUtil.isNuLLStr(sqlObject.getAdamsuninstallsql()))
			{
				fileUrl = Thread.currentThread().getContextClassLoader()
						.getResource(adamsuninstallsql);
				if (null != fileUrl)
				{
					try
					{
						String sqlScript = getSqlScript(fileUrl,
								adamsuninstallsql);
						sqlObject.setAdamsuninstallsql(sqlScript);
						sqlObject = execSqlScriptInfoService
								.saveOrUpdateExecSqlScriptInfo(sqlObject, null);
					} catch (BusinessException e)
					{
						// 执行脚本失败
						logger.error("保存" + adamsuninstallsql + "脚本入数据库出错！", e);
					} catch (Exception e)
					{
						logger.error("保存执行sql脚本信息对象失败！", e);
					}
				}
			}
		}

		// 反安装操作
		Set<String> dbbusinessnames = sqlMap.keySet();
		for (String dbbusinessname : dbbusinessnames)
		{
			// 存在不管
			if (businessnames.contains(dbbusinessname))
			{
				continue;
			}
			ExecSqlScriptInfo sqlObject = sqlMap.get(dbbusinessname);
			try
			{
				// 不存在就进行反安装操作
				// execSqlScriptInfoService.deleteExecSqlScriptInfo(sqlObject);
				// 修改反安装操作逻辑 只反安装install部分,不反安装init部分
				String sqlScript = sqlObject.getAdamsuninstallsql();
				sqlObject.setAdamsinstallsql(null);
				sqlObject.setAdamsuninstallsql(null);
				sqlObject = execSqlScriptInfoService
						.saveOrUpdateExecSqlScriptInfo(sqlObject, sqlScript);
			} catch (BusinessException e)
			{
				// 执行脚本失败
				logger.error("反安装" + dbbusinessname + "业务失败！", e);
			}
		}
	}

	private static String getSqlScript(URL fileUrl, String sql)
			throws IOException
	{
		StringBuilder sqlScript = new StringBuilder();
		try
		{
			BufferedReader br = new BufferedReader(new InputStreamReader(
					fileUrl.openStream(), "UTF-8"));
			String line = br.readLine();
			while (line != null)
			{
				sqlScript.append(line);
				sqlScript.append("\r\n");
				line = br.readLine();
			}
		} catch (IOException e)
		{
			// 执行脚本失败
			logger.error("读取" + sql + "脚步本失败！", e);
			throw e;
		}
		return sqlScript.toString();
	}
}
